package rs

import (
	"common/pb"
	"fmt"
	"github.com/zeebo/errs"
	"strconv"
	"strings"
)

var Error = errs.Class("rs_coder")

type Rs struct {
	Required  uint32
	Repair    uint32
	MinUpload uint32
	Total     uint32
}

func (rs *Rs) Valid() bool {
	if rs != nil && rs.Required > 0 && rs.Required <= rs.Repair && rs.Repair <= rs.MinUpload && rs.MinUpload <= rs.Total {
		return true
	}
	return false
}

// String returns uuid in "x/x/x/x" format.
func (rs Rs) String() string {
	return fmt.Sprintf("%d/%d/%d/%d", rs.Required, rs.Repair, rs.MinUpload, rs.Total)
}

func (rs Rs) Intval() uint32 {
	r := (rs.Required & 0xff) << 24
	r |= (rs.Repair & 0xff) << 16
	r |= (rs.MinUpload & 0xff) << 8
	r |= rs.Total & 0xff
	return r
}

func FromInt(r uint32) (Rs, error) {
	var rs Rs
	if r == 0 {
		return rs, nil
	}
	rs.Required = (r >> 24) & 0xff
	rs.Repair = (r >> 16) & 0xff
	rs.MinUpload = (r >> 8) & 0xff
	rs.Total = r & 0xff
	return rs, nil
}

// FromString parses "x/x/x/x" string form.
func FromString(s string) (Rs, error) {
	var rs Rs
	r := strings.SplitN(s, "/", 4)
	if len(r) != 4 {
		return rs, Error.New("%s parse error", s)
	}
	rm := [4]uint32{}
	for i := range r {
		n, err := strconv.ParseInt(r[i], 10, 8)
		if err != nil {
			return rs, Error.New("%s parse error", s)
		}
		rm[i] = uint32(n)
	}
	rs.Required = rm[0]
	rs.Repair = rm[1]
	rs.MinUpload = rm[2]
	rs.Total = rm[3]
	return rs, nil
}

func (rs Rs) MarshalText() ([]byte, error) {
	return []byte(rs.String()), nil
}

func (rs *Rs) UnmarshalText(b []byte) error {
	var err error
	*rs, err = FromString(string(b))
	if err != nil {
		return Error.Wrap(err)
	}
	return nil
}

func (rs Rs) MarshalJSON() ([]byte, error) {
	return []byte(strconv.Itoa(int(rs.Intval()))), nil
}

func (rs *Rs) UnmarshalJSON(b []byte) error {
	if len(b) == 0 {
		*rs = Rs{}
		return nil
	}
	n, err := strconv.Atoi(string(b))
	if err != nil {
		return Error.Wrap(err)
	}
	*rs, err = FromInt(uint32(n))
	return err
}

func ToPbRs(rs Rs) *pb.RS {
	return &pb.RS{
		Total:     rs.Total,
		Required:  rs.Required,
		MinUpload: rs.MinUpload,
		Repair:    rs.Repair,
	}
}

func PbToRs(rs *pb.RS) Rs {
	return Rs{
		Total:     rs.Total,
		Required:  rs.Required,
		MinUpload: rs.MinUpload,
		Repair:    rs.Repair,
	}
}
